//
// Created by Yun Zeng on 2018/4/17.
// 红外遥控发射
//

#include "platform_infrared.h"
#include "master.h"

#include <linux/gpio.h>
#include <linux/interrupt.h>

//#define INFRARED_INPUT_ENABLE

#ifdef INFRARED_INPUT_ENABLE
#define     INFRARED_INPUT_PIN       1
#define     INFRARED_INPUT_NAME      "infrared_input_pin"
#endif

#define     INFRARED_OUTPUT_PIN      19
#define     INFRARED_OUTPUT_NAME     "infrared_output_pin"

#define     INFRARED_BUFFER_SIZE     1024

typedef struct {
    // 红外接收相关
    int read_inter;
    struct timeval rev_interval;
    struct hrtimer rev_timer;
    int rev_buffer[INFRARED_BUFFER_SIZE];
    int rev_cnt;

    // 红外发送相关
    struct hrtimer send_timer;
    int send_buffer[INFRARED_BUFFER_SIZE];
    int send_cnt;
    int send_len;
    int send_state;
}infraredInfoType;

static infraredInfoType infraredInfo;

// 红外码发射
static enum hrtimer_restart infrared_send_handler(struct hrtimer *timer) {
    //printk(INFRARED_DEV_NAME ": send handler");
    static int vflag = 0;
    if (vflag ==0) {
        vflag = 1;
        hrtimer_forward_now(&infraredInfo.send_timer, ktime_set(0, 8680));
    } else {
        vflag = 0;
        hrtimer_forward_now(&infraredInfo.send_timer, ktime_set(0, 17520));
    }

    if (infraredInfo.send_cnt >= infraredInfo.send_len) {
        infraredInfo.send_cnt = 0;
        infraredInfo.send_len = 0;
        gpio_set_value(INFRARED_OUTPUT_PIN, 0);
        return HRTIMER_NORESTART;
    }

    if (infraredInfo.send_buffer[infraredInfo.send_cnt] == 0) {
        infraredInfo.send_cnt ++;
    }

    if (infraredInfo.send_buffer[infraredInfo.send_cnt] > 0) {
        infraredInfo.send_buffer[infraredInfo.send_cnt] -= 1;
        // 翻转
        if (vflag == 0) {
            gpio_set_value(INFRARED_OUTPUT_PIN, 0);
        } else {
            gpio_set_value(INFRARED_OUTPUT_PIN, 1);
        }

    } else if(infraredInfo.send_buffer[infraredInfo.send_cnt] <= 0) {
        infraredInfo.send_buffer[infraredInfo.send_cnt] += 1;
        gpio_set_value(INFRARED_OUTPUT_PIN, 0);

    }
    return HRTIMER_RESTART;
}

#ifdef INFRARED_INPUT_ENABLE
// 正在接收红外码
irqreturn_t infrared_input_handler(int irqno, void *dev_id) {
    struct timeval time;
    int time_interval;
    infraredInfo.rev_cnt += 1;
    if (infraredInfo.rev_cnt > INFRARED_BUFFER_SIZE) {
        infraredInfo.rev_cnt = 0;
    }

    do_gettimeofday(&time);
    if (infraredInfo.rev_cnt > 1) {
        time_interval = (int) ((time.tv_sec - infraredInfo.rev_interval.tv_sec) * 1000000  + (time.tv_usec - infraredInfo.rev_interval.tv_usec));
        if (gpio_get_value(INFRARED_INPUT_PIN) == 0) {
            infraredInfo.rev_buffer[infraredInfo.rev_cnt] = -time_interval;
        } else {
            infraredInfo.rev_buffer[infraredInfo.rev_cnt] = time_interval;
        }
    }

    // 超过300ms没有收到数据，即为红外接收完成
    if (infraredInfo.rev_timer.state == HRTIMER_STATE_INACTIVE) {
        hrtimer_start(&infraredInfo.rev_timer, ktime_set(0, 300000000), HRTIMER_MODE_REL);
    } else {
        hrtimer_forward_now(&(infraredInfo.rev_timer), ktime_set(0, 300000000));
    }

    infraredInfo.rev_interval.tv_sec = time.tv_sec;
    infraredInfo.rev_interval.tv_usec = time.tv_usec;
    return IRQ_HANDLED;
}


// 红外码接收完成
static enum hrtimer_restart infrared_rev_handler(struct hrtimer *timer) {
    int i;
    printk(MASTER_DEV_NAME ": rev len(%d)\n", infraredInfo.rev_cnt);
    for(i=0; i<infraredInfo.rev_cnt; i++) {
        printk("%d,", infraredInfo.rev_buffer[i]);
    }
    printk("\n");


    uint8_t ifbuffer[14] = {0x00};
    int offset = 0;

    // 转换编码
    for(i=6; i< infraredInfo.rev_cnt; i+=2) {
        if (infraredInfo.rev_buffer[i] < -3000 && i == 0) {
            printk("S");
        } else if (infraredInfo.rev_buffer[i] < -4000) {
            printk("S");
        } else if (infraredInfo.rev_buffer[i+1] < -1000 && infraredInfo.rev_buffer[i+1] >  -2000) {

            int x = 7 - offset % 8;
            int y = offset / 8;
            ifbuffer[y] |= (0x01 << x);
            offset += 1;
            printk("1");
        } else if (infraredInfo.rev_buffer[i+1] < 0 && infraredInfo.rev_buffer[i+1] > -1000) {
            printk("0");
            offset += 1;
        } else {
            printk("L ");
        }

        if (offset % 8 == 0) {
            printk(" ");
        }

    }
    printk("\n");

    uint8_t check = 0x00;
    for (i=0; i<13; i++) {
        printk("%02x", ifbuffer[i]);
        check += ifbuffer[i];
    }
    printk("%02x", ifbuffer[13]);
    printk(" [%02x]", check);

    printk("\n");
    printk("\n");
    infraredInfo.rev_cnt = 0;
    return HRTIMER_NORESTART;
}
#endif

// 将要发送的红外码写入到发送缓冲区
int32_t platform_infrared_write(uint8_t *data, uint16_t len) {
    int i;
    if (data == NULL || len % 2 != 0
        || len / 2 > sizeof(infraredInfo.send_buffer)/sizeof(infraredInfo.send_buffer[0])) {
        return -1;
    }

    infraredInfo.send_len = len/2;
    memset(infraredInfo.send_buffer, 0x00, sizeof(infraredInfo.send_buffer)/sizeof(infraredInfo.send_buffer[0]));

    printk(MASTER_DEV_NAME ": infrared send(");
    for (i=0; i<infraredInfo.send_len; i++) {
        infraredInfo.send_buffer[i] = (int16_t)(be16_to_cpu(*(int16_t *)&data[i*2]));
        printk("%d,", infraredInfo.send_buffer[i]);
    }
    printk(")\n");

    // 发射
    gpio_set_value(INFRARED_OUTPUT_PIN, 0);
    hrtimer_start(&infraredInfo.send_timer, ktime_set(0, 400000), HRTIMER_MODE_REL);
    return 0;
}



// 初始化引脚、定时器等
int platform_infrared_init(master_dev_typedef *dev) {
    int result;

#ifdef INFRARED_INPUT_ENABLE
    // 输入部分
    result = gpio_request(INFRARED_INPUT_PIN, INFRARED_INPUT_NAME);
    if (result < 0) {
        printk(MASTER_DEV_NAME ": gpio request(%d) failed\n", INFRARED_INPUT_PIN);
        return -1;
    }
    gpio_direction_input(INFRARED_INPUT_PIN);

    // gpio req
    infraredInfo.read_inter = gpio_to_irq(INFRARED_INPUT_PIN);
    result = request_irq(infraredInfo.read_inter,
                         infrared_input_handler,
                         IRQF_TRIGGER_MASK | IRQF_SHARED,
                         "infrared_input_inter",
                         &dev->devno);
    if (result < 0) {
        gpio_free(INFRARED_INPUT_PIN);
        printk(MASTER_DEV_NAME ": gpio_to_irq(%d) failed\n", INFRARED_INPUT_PIN);
        return -1;
    }
    printk(MASTER_DEV_NAME ": gpio irq enabled interrupt(%d)\n", infraredInfo.read_inter);

    hrtimer_init(&infraredInfo.rev_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    infraredInfo.rev_timer.function = infrared_rev_handler;
#endif
    // 输出部分
    result = gpio_request(INFRARED_OUTPUT_PIN, INFRARED_OUTPUT_NAME);
    if (result < 0) {
        printk(MASTER_DEV_NAME ": gpio request(%d) failed\n", INFRARED_OUTPUT_PIN);
        return 0;
    }
    gpio_direction_output(INFRARED_OUTPUT_PIN, 0);

    hrtimer_init(&infraredInfo.send_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    infraredInfo.send_timer.function = infrared_send_handler;
    infraredInfo.rev_cnt = 0;
    return 0;
}

int platform_infrared_exit(master_dev_typedef *dev) {
#ifdef INFRARED_INPUT_ENABLE
    hrtimer_cancel(&infraredInfo.rev_timer);
    gpio_free(INFRARED_INPUT_PIN);
    disable_irq(infraredInfo.read_inter);
    free_irq(infraredInfo.read_inter, &dev->devno);
#endif
    hrtimer_cancel(&infraredInfo.send_timer);
    gpio_free(INFRARED_OUTPUT_PIN);
    return 0;
}